EP17. 컨텍스트 증강과 RAG

LLM 입력 컨텍스트 예산을 관리하는 컨텍스트 엔지니어링의 관점에서 RAG의 표준 정의(임베딩 모델 + 코사인 유사도)와 검색 품질을 좌우하는 청킹 전략을 학습합니다.


0. 사전 필수 용어 (선행지식)

본 EP 를 이해하려면 다음 5개 용어를 먼저 숙지하세요. 깊은 선수 지식은 AI 엔지니어링 EP0 - 선수지식 또는 Hugging Face NLP Course 를 참고합니다.

  1. 컨텍스트 윈도우 (Context Window) LLM이 한 번에 입력으로 받을 수 있는 토큰 수 상한. "도서관에 있는 책상 한 칸에 한 번에 올릴 수 있는 책 더미 두께"에 비유할 수 있습니다. 본문 §1·§2 의 "입력 컨텍스트 예산" 용어와 직결됩니다.

  2. 임베딩 (Embedding) / 밀집 벡터 (Dense Vector) 문자열·이미지·사운드 등 입력을 의미를 보존하는 고정 길이 실수 벡터(예: 1536차원)로 변환한 결과. "단어를 의미 좌표계의 점으로 찍는 것"으로 비유합니다. KNN·로지스틱 회귀 등으로 만든 통계 벡터와 구분되며, 임베딩 모델은 신경망 학습 산물입니다. 본문 §3·§4 의 RAG 정의 조건에 반복 등장.

  3. 코사인 유사도 (Cosine Similarity) 두 벡터의 방향이 얼마나 같은가를 [-1, 1] 로 측정. "두 화살표가 같은 방향을 가리키면 1, 직각이면 0, 정반대면 -1". 임베딩 모델의 손실 함수가 이 척도로 설계돼 있어 RAG 의 표준 검색 거리 함수가 됩니다. 본문 §3 RAG 표준 조건·§6 핵심 원리에서 사용.

  4. 벡터 데이터베이스 (Vector DB) 임베딩 벡터를 저장하고 코사인 유사도 기반 ANN(근사 최근접 이웃) 검색을 빠르게 수행하는 저장소. Chroma·Qdrant·pgvector·Milvus·Weaviate 등이 대표적입니다. 본문 §4 의 "멀티벡터 스토어"·§5 의 OSS 인용에서 사용.

  5. 리트리버 (Retriever) 사용자 질의를 받아 임베딩화·검색·점수 필터링·재랭킹까지 한 인터페이스 뒤에 묶은 구성 요소. LangChain/LlamaIndex 에서 모듈 단위로 제공됩니다. 본문 §3·§7·§10 의 패턴 인용에서 사용.


1. 주제 정의

컨텍스트 증강(Context Augmentation) 은 LLM이 한 번에 받을 수 있는 입력 토큰 한도(컨텍스트 예산) 안에 사용자 질의·프롬프트·배경 지식·대화 기억·도구 사용 정보 등 응답 품질에 기여하는 모든 요소를 선별적으로 채워 넣는 설계 활동입니다. 이 활동을 체계화한 영역을 컨텍스트 엔지니어링(Context Engineering) 이라 부르며, RAG는 그 중 "배경 지식" 슬롯을 외부 검색으로 채우는 대표 기법입니다.

핵심 아이디어: 프롬프트 한 줄을 정교화하는 차원을 넘어, 제한된 입력 예산을 어디에 얼마나 배분할지 최적화하는 것이 RAG·도구 사용·메모리·페르소나를 통합 관리하는 출발점입니다.


2. 풀려는 문제

LLM을 실서비스에 투입하면 다음 4가지 컨텍스트 자원 충돌이 반복적으로 나타납니다.

  • 문제 1 — 컨텍스트 예산 초과: MCP 서버 200개·긴 대화 기억·도구 설명을 모두 넣으면 정작 배경 지식 토큰이 부족해 응답 품질이 무너집니다.
  • 문제 2 — 환각(Hallucination): 모델 학습 시점 이후 정보, 사내 도메인 데이터, 실시간 가격·날씨 등을 알지 못해 그럴듯한 거짓을 답변합니다.
  • 문제 3 — 검색 품질 저하: RAG를 도입해도 청킹이 거칠면 "잘린 문장 조각"이 임베딩 되어 의미론적 검색이 사용자 의도를 못 잡습니다.
  • 문제 4 — 멀티미디어 입력 한도: 한 장의 큰 이미지·긴 사운드·동영상도 모델별 인풋 사이즈 제한이 있어 그대로 넣을 수 없습니다.

💡 실무 노하우: 컨텍스트 예산은 단순히 "토큰 수"가 아니라 비용·지연시간·정확도의 3축 트레이드오프입니다. 입력이 길어지면 (a) 비용 선형 증가, (b) Time-to-First-Token 증가, (c) Lost-in-the-Middle 현상으로 중간 위치 정보 회상률이 떨어집니다. 예산을 무작정 키우기보다 우선순위 큐로 슬롯을 관리하세요.


3. 핵심 개념·구조

컨텍스트 증강 시스템은 다음 요소로 구성됩니다.

  • 입력 컨텍스트 슬롯: 시스템 프롬프트, 유저 쿼리, 대화 기록, 사용자 프로필, 도구 정의, RAG 검색 결과, 실시간 정보.
  • 컨텍스트 예산(Context Budget): 모델 카드의 max input tokens 에서 출력 예약분을 뺀 잔여 토큰량.
  • RAG 컴포넌트: Document Loader → ChunkerEmbedderVector StoreRetriever → (선택) Re-ranker → LLM.
  • 선택 옵션: 리랭커, 메타 필터 검색, 본문 키워드(BM25/LIKE) 결합, 사용자 질의 재작성(Query Rewriting), 멀티벡터 스토어.
┌────────────────────────────────────────────────┐
│  Context Budget (예: 100K tokens)              │
├────────────────────────────────────────────────┤
│ [system] [user query] [history] [profile]      │
│ [tool defs] [RAG hits] [realtime] [response ↓] │
└────────────────────────────────────────────────┘
            │
            ▼ RAG 슬롯 채우기
┌──────────────────────────────────────┐
│ user query                           │
│    │  (선택) query rewrite (LLM)     │
│    ▼                                 │
│ embed → vector DB → top-k chunks     │
│    │                                 │
│    ▼ (선택) re-rank (LLM/cross-enc)  │
│ context payload  ─────► prompt       │
└──────────────────────────────────────┘

업계는 (a) 임베딩 모델로 밀집 벡터 생성 + (b) 코사인 유사도 기반 검색 — 이 두 조건을 만족하는 시스템만을 RAG(=시맨틱 서치)로 인정합니다.


4. 구현 가이드 (Do It Yourself)

시작 전 (Before you begin)

이 섹션을 완료하면 PDF 한 권을 청킹·임베딩·검색하여 LLM 답변에 주입하는 최소 RAG 파이프라인을 직접 만들 수 있습니다.

선수 조건: - Python 3.10 이상 - pip install langchain langchain-community langchain-openai langchain-anthropic chromadb pypdf sentence-transformers - 환경 변수: ANTHROPIC_API_KEY (또는 OPENAI_API_KEY)

소요 시간: 약 25분.


Step 1 — 문서 로딩과 청킹 전략 정하기

목표: PDF 한 권을 임베딩 모델 가이드라인에 맞춘 길이로 분할합니다.

다음 코드를 rag_pipeline.py 에 추가합니다.

from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter

loader = PyPDFLoader("docs/ai_engineering_book.pdf")
documents = loader.load()

splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,
    chunk_overlap=200,
    separators=["\n\n", "\n", ". ", " ", ""],
)
chunks = splitter.split_documents(documents)
print(f"총 {len(chunks)}개 청크 생성")

이 코드는 PDF를 페이지 단위 Document 로 읽고, RecursiveCharacterTextSplitter 가 단락→문장→공백 순으로 의미 경계를 우선해 분할합니다. chunk_size=1000·chunk_overlap=200 은 출발점이며, 실제 값은 사용할 임베딩 모델 카드의 권장치를 따르세요.

⚠️ 주의: 청크 길이는 임의로 정하지 마세요. 임베딩 모델마다 "어떤 길이에서 검색 일치도가 가장 높은지" 권장 값이 다릅니다. OpenAI text-embedding-3-small·Cohere embed-multilingual-v3·BAAI/bge-m3 모두 권장치가 다릅니다.

💡 실무 노하우: 청크가 너무 길면 컨텍스트 예산을 크게 잠식하고 핵심 정보 추출 난도가 오릅니다. 너무 짧으면 한 청크가 독립적 의미를 잃어 검색은 되어도 답변 생성에 실패합니다. 한국어 기술 문서 기준 600~1200자 + 10~20% overlap 으로 시작해 검색 품질 지표(nDCG@k)로 조정하세요.

📚 참고: LangChain의 Text Splitters 문서와 LlamaIndex의 NodeParser 가이드.

확인: print 출력으로 청크 수가 0보다 크고, 임의의 chunks[0].page_content 가 완결된 문장 흐름을 포함하면 성공입니다.


Step 2 — 임베딩 + 벡터 DB 색인

목표: 청크를 밀집 벡터로 변환해 코사인 유사도 검색이 가능한 벡터 DB 에 저장합니다.

from langchain_huggingface import HuggingFaceEmbeddings
from langchain_chroma import Chroma

embeddings = HuggingFaceEmbeddings(
    model_name="BAAI/bge-m3",
    model_kwargs={"device": "cuda"},
    encode_kwargs={"normalize_embeddings": True},
)

vectorstore = Chroma.from_documents(
    documents=chunks,
    embedding=embeddings,
    collection_metadata={"hnsw:space": "cosine"},
    persist_directory="./chroma_db",
)

normalize_embeddings=Truehnsw:space="cosine" 조합으로 코사인 유사도 검색이 활성화됩니다. BAAI/bge-m3 는 한국어·영어를 한 모델로 처리하며 100+ 언어를 지원합니다.

⚠️ 주의: 코사인 유사도를 쓰는 이유는 임베딩 모델의 손실 함수가 코사인 유사도로 학습되었기 때문입니다. 유클리드 거리·내적은 동작은 하지만 모델 학습 분포와 어긋나 품질이 떨어질 수 있습니다.

📚 참고: HuggingFace Sentence Transformers, Chroma 공식 문서.

확인: ./chroma_db/ 디렉토리가 생성되고 .sqlite3 파일이 100KB 이상이면 색인이 영속화된 것입니다.


Step 3 — 리트리버 + LLM 응답 생성

목표: 사용자 질의를 받아 top-k 청크를 검색하고 LLM 응답에 주입합니다.

from langchain_anthropic import ChatAnthropic
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser

retriever = vectorstore.as_retriever(search_kwargs={"k": 4})

prompt = ChatPromptTemplate.from_template(
    "다음 컨텍스트만 근거로 답하세요. 모르면 모른다고 답하세요.\n"
    "<context>\n{context}\n</context>\n\n질문: {question}"
)

llm = ChatAnthropic(model="claude-sonnet-4-6", temperature=0.2, max_tokens=1024)

def format_docs(docs):
    return "\n\n".join(d.page_content for d in docs)

chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

print(chain.invoke("청킹 길이는 어떻게 결정하나요?"))

k=4 는 출발 값입니다. 본문 검색(BM25) 결합·메타 필터·리랭킹 도입 전 단순 의미론적 검색만으로 베이스라인을 측정하세요.

💡 실무 노하우: LangChain 의 LCEL(LangChain Expression Language) | 연산자로 컴포넌트를 합성하면 스트리밍·배치·async 가 모두 자동 지원됩니다. 같은 체인을 chain.stream(...)·chain.abatch([...]) 로 즉시 사용할 수 있습니다.

📚 참고: Anthropic Messages API, LangChain LCEL 가이드.

확인: 응답에 PDF 본문에서 등장한 고유 용어가 인용되어 있다면 RAG 가 정상 작동한 것입니다.


Step 4 — 동작 확인 (테스트)

queries = [
    "컨텍스트 엔지니어링이란 무엇인가?",
    "RAG 가 업계에서 인정받기 위한 두 조건은?",
    "청킹 길이를 결정하는 가장 중요한 기준은?",
]
for q in queries:
    print(f"\nQ: {q}")
    print(f"A: {chain.invoke(q)[:200]}...")

예상 출력 (요지):

Q: RAG 가 업계에서 인정받기 위한 두 조건은?
A: (1) 임베딩 모델로 밀집 벡터를 생성할 것, (2) 검색 시 코사인 유사도 기반 알고리즘을 사용할 것 ...

세 질의 모두 PDF 본문 근거 인용이 보이면 최소 파이프라인이 완성된 것입니다.


5. 적용 사례 (공신력 오픈소스)

  • LangChain (https://github.com/langchain-ai/langchain) — Chain·Retriever·Splitter 표준 인터페이스 제공.
  • LlamaIndex (https://github.com/run-llama/llama_index) — RAG·인덱싱·NodeParser 전문 프레임워크.
  • Anthropic Python SDK (https://github.com/anthropics/anthropic-sdk-python) — Claude Messages API 공식 SDK.
  • OpenAI Python SDK (https://github.com/openai/openai-python) — Embeddings·Chat Completions 공식 SDK.
  • HuggingFace Transformers (https://github.com/huggingface/transformers) — 임베딩 모델 호스팅·로컬 추론.
  • vLLM (https://github.com/vllm-project/vllm) — PagedAttention 기반 고성능 LLM 서빙(RAG 백엔드).
  • Ollama (https://github.com/ollama/ollama) — 로컬 LLM·임베딩 모델 단일 바이너리 실행.
  • LiteLLM (https://github.com/BerriAI/litellm) — 100+ 프로바이더 통합 게이트웨이.
  • Spring AI (https://github.com/spring-projects/spring-ai) — JVM 진영의 RAG·Embedding 추상화.
  • LangChain4j (https://github.com/langchain4j/langchain4j) — Java/Kotlin LangChain 포트.

📚 참고: 위 OSS 는 모두 GitHub stars 5K 이상 또는 Apache·Anthropic·OpenAI 공식 SDK 입니다. 한국어 RAG 패턴 사례는 teddylee777/langchain-kr 등 한국어 튜토리얼 레포를 함께 참고하세요.

💡 실무 노하우: LangChain 의 LCEL 같은 메타 패턴은 컴포넌트 간 결합도를 낮춰 청킹·임베딩·리트리버·리랭커를 부품처럼 교체하게 해 줍니다. RAG 실험기에서는 LCEL 또는 LlamaIndex QueryEngine 같은 합성 API 로 베이스라인을 빠르게 만든 뒤 측정·최적화에 시간을 더 쓰세요.


6. 핵심 원리

  1. 컨텍스트는 예산이다. 모든 정보 슬롯이 같은 토큰 풀을 두고 경쟁합니다. RAG·도구·기억은 서로 빼앗는 관계이며, 우선순위 큐와 토큰 회계가 컨텍스트 엔지니어링의 본질입니다.
  2. RAG = 임베딩 + 코사인 유사도. 업계 표준 정의는 이 두 조건만으로 성립합니다. 손실 함수와 검색 거리 함수가 같을 때만 "의미론적 검색" 이라는 이름이 의미를 갖습니다.

7. 변형·확장

  • 하이브리드 검색(Hybrid Retrieval): 밀집 벡터(의미론적) + 희소 벡터/BM25(키워드) 결과를 RRF(Reciprocal Rank Fusion) 으로 합쳐 정확도와 사용자 체감 적합도를 함께 끌어올립니다.
  • 메타 필터 검색: where={"category": "AI"} 같은 JSON 필드 조건으로 검색 공간을 좁힌 뒤 벡터 유사도를 적용합니다.
  • 질의 재작성(Query Rewriting / HyDE): 모호한 사용자 질의를 LLM이 가설 응답으로 변환하고 그 벡터로 검색하면 회상률이 오릅니다.
  • 멀티벡터 스토어: 동일 청크에 다양한 임베딩(요약·키워드·원문)을 함께 저장하거나, 동일 임베딩에 다양한 본문 변형을 연결하는 두 방향 모두 지원합니다.
  • 에이전트형 RAG(Agentic RAG): 검색을 한 번이 아닌 다단계 도구 호출로 반복하며 답이 충분해질 때까지 자율 결정합니다.

8. 다른 도구·접근과의 비교 (3-way)

  • vs Fine-tuning: Fine-tuning 은 모델 파라미터에 지식을 굽지만 변경·롤백 비용이 큽니다. RAG는 외부 색인만 갱신하므로 일·시간 단위 데이터 변화를 즉시 반영합니다.
  • vs Long-Context 모델(1M+ 토큰): 긴 컨텍스트 모델은 "넣기만 하면 알아서 찾는다" 는 환상이 있지만 Lost-in-the-Middle·비용·지연 비효율이 큽니다. RAG는 top-k 추출로 비용을 1~2 자릿수 줄입니다.
  • vs Knowledge Graph/그래프 RAG: 사실 관계·다단 추론에 강합니다. 일반 자연어 문답·롱테일 도메인 지식엔 의미론적 RAG 가 구현 비용 대비 효과적이며, 그래프 RAG 는 LlamaIndex/Microsoft GraphRAG 처럼 별도 구축 단계가 필요합니다.

9. 한계·트레이드오프

  1. 검색 품질이 청킹에 종속: 청킹이 거칠면 어떤 임베딩·리트리버를 써도 무너집니다. RAG 시스템 디버깅 시간의 절반 이상이 청킹에 들어가는 일이 흔합니다.
  2. 임베딩 모델 락인(Lock-in): 한 모델로 색인한 벡터는 다른 모델 벡터와 의미가 다르므로 모델 교체 시 전체 재색인 비용이 발생합니다.
  3. 신선도(Freshness)와 인덱스 운영: 원천 문서가 빠르게 바뀌면 색인 갱신 파이프라인(증분·삭제·중복 제거)을 별도 운영해야 합니다.
  4. 보안·PII: 사내 문서가 벡터화되어 외부 임베딩 API 로 흘러가면 정보 유출 위험. 로컬 임베딩(BGE/Sentence-Transformers + Ollama)·온프레미스 벡터 DB 검토가 필요합니다.

10. 최신 권장 패턴 (2025 이후)

  • 하이브리드 + RRF 결합: 의미론적 + BM25/키워드 결합이 단일 RAG 보다 사용자 체감 적합도가 높습니다.
  • Cross-Encoder Re-ranker: BAAI/bge-reranker-v2-m3·Cohere Rerank 등으로 top-50 → top-5 재정렬해 정밀도를 끌어올립니다.
  • 에이전트형 RAG + Tool Use: LangGraph·LlamaIndex AgentWorkflow 로 "검색-판단-재검색" 루프를 명시적으로 설계합니다.
  • 구조화 출력(Structured Output): Anthropic Tool Use·OpenAI Structured Outputs 로 RAG 답변을 JSON 스키마로 강제해 후처리 안정성을 확보합니다.
  • MCP(Model Context Protocol) 통한 외부 RAG 연결: 같은 RAG 인덱스를 여러 LLM·에이전트가 표준 프로토콜로 공유합니다 (2026-05 기준, Anthropic MCP 공식 문서).
  • Multimodal RAG: 텍스트 외 이미지(CLIP·SigLIP)·오디오(스펙트럼 임베딩)도 동일 벡터 공간 또는 별도 컬렉션으로 색인하고 멀티모달 LLM 으로 합성합니다.

11. 메타인지 자기평가

본인 코드/시스템에 RAG 컨텍스트 증강을 적용할 수 있는지 다음 절차로 검증합니다.

Step 1 — 현재 상태 점검

# 사내 문서 후보 디렉토리 크기·파일 수 확인
du -sh ./docs ./wiki ./manuals 2>/dev/null
find ./docs -name "*.pdf" -o -name "*.md" | wc -l

# 현재 프롬프트가 모델 컨텍스트 윈도우 중 얼마를 쓰는지 측정
python -c "import tiktoken; enc=tiktoken.encoding_for_model('gpt-4o'); print(len(enc.encode(open('prompts/system.txt').read())))"

Step 2 — 적용 가능성 평가

  • 도메인 지식이 모델 학습 시점 이후이거나 사내·비공개 문서인가?
  • 응답마다 인용·근거가 필요한 시나리오(법무·의료·고객 지원)인가?
  • 데이터 신선도 요구가 시간·일 단위인가? (실시간 가격은 RAG 보다 Tool Use 가 적합)
  • 임베딩·벡터 DB 운영 인프라(스토리지·재색인 파이프라인)를 감당할 수 있는가?

Step 3 — 점진 적용

  1. 가장 자주 들어오는 FAQ 10개로 정답 셋(평가 셋)을 먼저 만든다.
  2. 텍스트 PDF/마크다운만으로 베이스라인 RAG(§4 코드)를 구축하고 Recall@k·nDCG@k 측정.
  3. 청크 사이즈·overlap·임베딩 모델을 한 번에 한 변수씩 변경하며 측정 비교.
  4. 그 다음에야 리랭커·하이브리드·메타 필터를 추가하고 다시 측정.
  5. 마지막으로 멀티모달·에이전트형 RAG 로 확장.

(검증일 2026-05-27, 본 EP 내용 + LangChain·LlamaIndex·Anthropic 공식 문서 기준)

난이도
에피소드
질문
카드를 로딩 중...
답변

클릭하거나 Space를 눌러 뒤집기

0 / 0
학습 진도 0%
이동   Space 뒤집기   R 셔플